const fs = require('fs');
const cluster = require('cluster');
const os = require('os');

var monitor = function (options = {}) {
  if (!(this instanceof monitor)) {
    return new monitor(options);
  }

  this.config = {
    type : 'text',
    file : '',
    debug : false
  };

  if (typeof options === 'object') {
    for (let k in options) {
      switch (k) {
        case 'debug':
        case 'type':
        case 'file':
          this.config[k] = options[k]; break;

        default:;
      }
    }
  }

  this.rundata = null;
  this.workers = null;
  this.sendInterval = null;
};


/**保存进程负载情况 */
monitor.prototype.loadInfo = {};
monitor.prototype.loadCount = 0;

monitor.prototype.fmtLoadInfo = function (type = 'text') {
  let oavg = os.loadavg();
  let p = null;

  if (type == 'text') {
    let oscpu = `CPU Loadavg  1m: ${oavg[0].toFixed(2)}  `
                + `5m: ${oavg[1].toFixed(2)}  15m: ${oavg[2].toFixed(2)}\n`;

    let cols = ' PID      CPU      CONN   MEM,   HEAP,  USED,  EXTERNAL\n';
    let tmp = '';
    let t = '';
    let p = null;

    for (let id in this.workers) {
      
      p = this.workers[id];

      tmp = ` ${p.pid}      `;
      tmp = tmp.substring(0, 10);

      t = p.cpu.user + p.cpu.system;
      t = (t/100000).toFixed(2);
      tmp += t + '%      ';
      tmp = tmp.substring(0, 20);

      tmp += `${p.conn}       `;
      tmp = tmp.substring(0,26);

      tmp += (p.mem.rss / 1048576).toFixed(1) + ',  ';
      tmp += (p.mem.heapTotal / 1048576).toFixed(1) + ',  ';
      tmp += (p.mem.heapUsed / 1048576).toFixed(1)+',   ';
      tmp += (p.mem.external / 1048576).toFixed(1);
      tmp += 'M';

      cols += `${tmp}\n`;
    }
    cols += `  Master PID: ${process.pid}\n`;
    cols += `  Listen ${this.rundata.host}:${this.rundata.port}\n`;

    return `${oscpu}${cols}`;
  }

  if (type == 'json') {
    let loadjson = {
      masterPid : process.pid,
      listen : `${this.rundata.host}:${this.rundata.port}`,
      CPULoadavg : {
        '1m' : `${oavg[0].toFixed(2)}`,
        '5m' : `${oavg[1].toFixed(2)}`,
        '15m' : `${oavg[2].toFixed(2)}`
      },
      workers : []
    };
    for (let id in this.workers) {
      p = this.workers[id];

      loadjson.workers.push({
        pid : p.pid,
        cpu : `${((p.cpu.user + p.cpu.system)/100000).toFixed(2)}%`,
        mem : {
          rss : (p.mem.rss / 1048576).toFixed(1),
          heap : (p.mem.heapTotal / 1048576).toFixed(1),
          heapused : (p.mem.heapUsed / 1048576).toFixed(1),
          external :  (p.mem.external / 1048576).toFixed(1),
        },
        conn : p.conn
      });
    }
    return JSON.stringify(loadjson);
  }

  if (type == 'orgjson') {
    let loadjson = {
      masterPid : process.pid,
      listen : `${this.rundata.host}:${this.rundata.port}`,
      CPULoadavg : {
        '1m' : `${oavg[0].toFixed(2)}`,
        '5m' : `${oavg[1].toFixed(2)}`,
        '15m' : `${oavg[2].toFixed(2)}`
      },
      workers : this.workers
    };
    return JSON.stringify(loadjson);
  }
  
  return '';
};

/**
 * 通过loadInfo保存的数据计算并显示进程和系统的负载情况。
 * 这个函数只能在Master进程中调用。
 * @param {object} w 子进程发送的数据。
 */
monitor.prototype.showLoadInfo = function (w, id) {

  if (this.config.type == '--null') {
    return;
  }

  if (this.workers[id] === undefined) {
    return ;
  }

  let total = Object.keys(cluster.workers).length;

  this.workers[id].cpu.user = w.cpu.user;
  this.workers[id].cpu.system = w.cpu.system;
  this.workers[id].mem.rss = w.mem.rss;
  this.workers[id].mem.heapTotal = w.mem.heapTotal;
  this.workers[id].mem.heapUsed = w.mem.heapUsed;
  this.workers[id].mem.external = w.mem.external;
  this.workers[id].conn = w.conn;

  this.loadCount += 1;
  if (this.loadCount < total) {
    return ;
  }
  
  let loadText = this.fmtLoadInfo( this.config.type );

  if (this.config.file == '' && process.ppid > 1) {
    console.clear();
  }

  if (this.config.file.length > 0) {
    fs.writeFile(this.config.file, loadText, (err) => {
      if (err && this.config.debug)
        console.error(err.message);
    });
  } else if (process.ppid > 1) {
    console.log(loadText);
  }

  this.loadCount = 0;
};

monitor.prototype.workerStart = function () {
  if (cluster.isMaster) {
    return ;
  }

  let self = this;
  self.sendInterval = setInterval(() => {
    self.rundata.cpuTime = process.cpuUsage(self.rundata.cpuLast);
    self.rundata.mem = process.memoryUsage();
    process.send({
      type : 'load',
      pid  : process.pid,
      cpu  : self.rundata.cpuTime,
      mem  : self.rundata.mem,
      conn : self.rundata.conn
    });
    this.rundata.cpuLast = process.cpuUsage();
  }, 1000);
};

monitor.prototype.msgEvent = function () {
  if (cluster.isWorker) {
    return;
  }

  let self = this;
  return (worker, msg) => {
    self.showLoadInfo(msg, worker.id);
  };
};

monitor.prototype.init = function (app) {
  this.rundata = app.rundata;
  this.workers = app.workers;

  app.setMsgEvent('load', this.msgEvent());
  this.workerStart();
};

module.exports = monitor;
